package fr.bda.kanban.util;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.xml.bind.JAXB;

import fr.bda.kanban.Project;
import fr.bda.kanban.Step;
import fr.bda.kanban.Task;
import fr.bda.kanban.Template;
import fr.bda.kanban.Parameter;

public class KanbanDAO {

	/**
	 * To create a new project
	 * 
	 * @param projectDir
	 *            The working folder (Where the project must be saved)
	 * @param projectName
	 *            The project name
	 * @param projectDescription
	 *            The project description
	 * 
	 * @return The project created
	 */
	public Project addProject(String projectDir, String projectName, String projectDescription) {

		String newProjectUuid = UUID.randomUUID().toString();
		File projectFile = new File(new File(projectDir), "project_" + newProjectUuid + ".xml");

		Project newProject = new Project();

		newProject.getConfiguration().setUuid(newProjectUuid);
		newProject.getConfiguration().setName(projectName);
		newProject.getConfiguration().setDescription(projectDescription);
		newProject.getConfiguration().setPath(projectFile.getAbsolutePath());

		projectFile.getParentFile().mkdirs();
		JAXB.marshal(newProject, projectFile);

		return newProject;
	}

	/**
	 * To add a step in the project
	 * 
	 * @param project
	 *            The project object where the step must be added
	 * @param stepDir
	 *            where the step folder is located
	 * @param stepName
	 *            The name of the step to add
	 * @return The created step
	 */
	public Step addStep(Project project, String stepName, String stepDescription) {

		String projectUuid = project.getConfiguration().getUuid();
		String newStepUuid = UUID.randomUUID().toString();
		File stepFolder = new File(new File(project.getConfiguration().getPath()).getParentFile(), "step_" + newStepUuid);
		File stepFile = new File(stepFolder, "step_" + projectUuid + "_" + newStepUuid + ".xml");

		Step newStep = new Step();

		newStep.getConfiguration().setUuid(newStepUuid);
		newStep.getConfiguration().setName(stepName);
		newStep.getConfiguration().setDescription(stepDescription);
		newStep.getConfiguration().setPath(stepFile.getAbsolutePath());

		stepFolder.mkdirs();

		JAXB.marshal(newStep, stepFile);

		project.getSteps().add(newStep);

		return newStep;
	}

	public Template addTemplate(Project project, String templateName, String templateDescription, Parameter... parameters) {

		String projectUuid = project.getConfiguration().getUuid();
		String newTemplateUuid = UUID.randomUUID().toString();
		File templateFile = new File(new File(project.getConfiguration().getPath()).getParentFile(), "template_" + projectUuid + "_" + newTemplateUuid + ".xml");

		Template newTemplate = new Template();

		newTemplate.setUuid(newTemplateUuid);
		newTemplate.setName(templateName);
		newTemplate.setDescription(templateDescription);
		newTemplate.setPath(templateFile.getAbsolutePath());
		if (parameters != null && parameters.length > 0) {
			newTemplate.getParameters().addAll(Arrays.asList(parameters));
		}

		JAXB.marshal(newTemplate, templateFile);

		project.getTemplates().add(newTemplate);

		return newTemplate;

	}

	/**
	 * To add a task in the project
	 * 
	 * @param project
	 *            The project
	 * @param taskName
	 *            The task name
	 * @param taskDescription
	 *            The task description
	 * @return The added task
	 */
	public Task addTask(Project project, String taskName, String taskDescription, Template... template) {

		String projectUuid = project.getConfiguration().getUuid();
		String newTaskUuid = UUID.randomUUID().toString();
		File taskFile = new File(new File(project.getConfiguration().getPath()).getParentFile(), "task_" + projectUuid + "_" + newTaskUuid + ".xml");

		Task newTask = new Task();

		newTask.getConfiguration().setUuid(newTaskUuid);
		newTask.getConfiguration().setName(taskName);
		newTask.getConfiguration().setDescription(taskDescription);
		newTask.getConfiguration().setPath(taskFile.getAbsolutePath());
		if (template != null && template.length > 0 && template[0] != null) {
			newTask.getConfiguration().setTemplate(template[0]);
			newTask.getConfiguration().setTemplatePath(template[0].getPath());
		}
		JAXB.marshal(newTask, taskFile);

		project.getTasks().add(newTask);

		return newTask;

	}

	/**
	 * To move a task to a step
	 * 
	 * @param task
	 *            The task to move
	 * @param step
	 *            The step where the task must be moved
	 */
	public void moveTask(Task task, Step step) {

		File taskFile = new File(task.getConfiguration().getPath());
		String taskFileName = taskFile.getName();

		File stepDir = new File(step.getConfiguration().getPath());

		File newTaskFile = new File(stepDir.getParentFile(), taskFileName);
		task.getConfiguration().setPath(newTaskFile.getAbsolutePath());

		taskFile.renameTo(newTaskFile);
		JAXB.marshal(task, newTaskFile);

	}

	public <T> List<T> listObject(final Class<T> type, final String folderPath, final String projectUuid) {

		List<T> res = new ArrayList<>();

		for (File f : Arrays.asList(new File(folderPath).listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.getName().startsWith(type.getSimpleName().toLowerCase() + "_" + projectUuid);
			}
		}))) {
			res.add(JAXB.unmarshal(f, type));
		}
		return res;
	}

	public Project loadProject(String projectPath) {

		Project p = JAXB.unmarshal(new File(projectPath), Project.class);

		// Load all task of the project not already in steps
		p.getTasks().addAll(listObject(Task.class, new File(p.getConfiguration().getPath()).getParent(), p.getConfiguration().getUuid()));

		p.getTemplates().addAll(listObject(Template.class, new File(p.getConfiguration().getPath()).getParent(), p.getConfiguration().getUuid()));
		Map<String, Template> templateMap = new HashMap<>();
		for (Template t : p.getTemplates()) {
			templateMap.put(t.getPath(), t);
		}

		for (File stepDir : new File(p.getConfiguration().getPath()).getParentFile().listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.isDirectory();
			}
		})) {

			// Load all task of the project in this step
			p.getTasks().addAll(listObject(Task.class, stepDir.getAbsolutePath(), p.getConfiguration().getUuid()));

			// Load all step of the project
			p.getSteps().addAll(listObject(Step.class, stepDir.getAbsolutePath(), p.getConfiguration().getUuid()));

		}

		for (Task t : p.getTasks()) {
			t.getConfiguration().setTemplate(templateMap.get(t.getConfiguration().getTemplatePath()));
		}
		return p;
	}
}
